#include "symTree.h"
#include <stdio.h>
#include <strings.h>
#include <stdlib.h>

int LocationCnt=0;

void printNicelySymTree(SYMTREE tree, int count){
	if(tree == NULL) printf(";The sym doesn't exist.\n");
	if(tree->nodeType != AT_FUNC)
		printf("; An unexpected node[type = %s] appear in the tree.\n",
											humanReadableNodeType(tree->nodeType));

	//print the current func node
	int i;
	for(i=0; i<count; i++)
		printf("\t");
	if(count)
		printf(";|-->");
	else
		printf(";");
	printf("[%s] name = %s type = %s forward = %s parent = %p next = %p previous = %p\n",
			humanReadableNodeType(tree->nodeType),tree->sval,humanReadableNodeType(tree->typeVal),
			(tree->isForward)?"Y":"N", tree->parent, tree->next,tree->previous);

	//print args
	SYMTREE arg = tree->args;
	while (arg != NULL) {
		for(i=0; i<=count; i++)
			printf("\t");
		printf(";|-->[%s] name = %s type = %s next = %p\n",humanReadableNodeType(arg->nodeType),
				arg->sval,humanReadableNodeType(arg->typeVal), arg->next);
		arg = arg->next;
	}
	//print var
	SYMTREE var = tree->var;
	while (var != NULL) {
		for(i=0; i<=count; i++)
			printf("\t");
		printf(";|-->[%s] name = %s type = %s next = %p\n",humanReadableNodeType(var->nodeType),
						var->sval,humanReadableNodeType(var->typeVal), var->next);
		var = var->next;
	}
	//print subfunction
	SYMTREE func = tree->func;
	while (func != NULL) {
		printNicelySymTree(func,count+1);
		func = func->next;
	}
}

void printSymTree(SYMTREE tree) {
	printNicelySymTree(tree,0);
}


SYMTREE createSymTree() {
	SYMTREENODE * node = (SYMTREENODE *) malloc(sizeof(SYMTREENODE));

	if (node == NULL) {
		fprintf(stderr, "KO\n");
		printf("ERROR : cannot malloc in createSymTreeNode()\n");
		exit(1);
	}

	node->nodeType = AT_UNKNOWN;
	node->sval = NULL;
	node->typeVal = 0;

	node->ival = 0;
	node->bval = UNDIF_BOOL;
	node->isForward = 0;

	node->args = NULL;
	node->func = NULL;
	node->var = NULL;
	node->parent = NULL;
	node->next = NULL;
	node->previous = NULL;
	node->ASTaddr=NULL;
    node->location=0;



	return node;
}

SYMTREE searchForwardFunction(SYMTREE sym, SYMTREE node){
	// NULL check
	if (node == NULL || sym == NULL)
		return NULL;

	int symType = sym->nodeType;
	int nodeType = node->nodeType;

	// When the AST node is AT_FUNC the strcmp between AST and sym are : node->left->right->sval
	// When the AT_FUNC_CALL node is given as parameter the lookup is different
	if ( nodeType == AT_FUNC )
	{
		if (symType != AT_FUNC && (nodeType != AT_FUNC || nodeType != AT_FUNC_CALL) )
			return NULL;

		SYMTREE subFunction = sym->func;
		while (subFunction != NULL) {
			if(strcmp(subFunction->sval,node->sval)==0 &&
					subFunction->typeVal == node->typeVal &&
					sameArgs(subFunction->args,node->args))
				return subFunction;

			subFunction = subFunction->next;
		}
	}
	return NULL;
}

/** Add a valid node to a sym tree
 * the deeper arguments is used only for add of function.
 * If it's 1 the function will be add as a subfunction otherwise it will be add at the same level
 */
int add(SYMTREE tree, SYMTREE node, int deeper) {

	// NULL check
	if (node == NULL)
		return Error(2417, "You cannot add an null node,");
	if (tree == NULL)
		return Error(2418, "You cannot add a node in a null tree,");

	int nodeType = node->nodeType;
	int treeType = tree->nodeType;

	//if the node is not initialised it mean it's a root node
	if (treeType == AT_UNKNOWN) {
		tree->nodeType = node->nodeType;
		tree->sval = node->sval;
		tree->typeVal = node->typeVal;
		tree->ival = node->ival;
		tree->bval = node->bval;
		tree->isForward = node->isForward;
		tree->args = node->args;
		tree->func = node->func;
		tree->var = node->var;
		tree->parent = node->parent;
		
	 char myBuf[20]; 
   sprintf(myBuf, "%p",  node );    
	 tree->ASTaddr=myBuf;
	 
		return 0;
	}

    tree->location=LocationCnt;
		LocationCnt++;
	//adapt the scope for each element
	if(nodeType == AT_FUNC){
		node->scope = tree->scope++;

	}else{
		node->scope=tree->scope;
	}

	//check invalid add operation
	if (treeType == AT_FUNC) {
		if (nodeType != AT_ARGS && nodeType != AT_DECLARE && nodeType != AT_FUNC)
			return Error(2402,
					"You cannot add this type of node to a function node.");
	} else if (treeType != nodeType) {
		return Error(2403,
				"You cannot add this type of node to a non function node.");
	}

	//if on function node
	if (treeType == AT_FUNC) {
		// if add the first arg to the function
		if (nodeType == AT_ARGS) {
			if (tree->args == NULL) {
				tree->args = node;
				return 0;
			} else {
				return add(tree->args, node,0);
			}
		}
		//if add the first var for the function
		if (nodeType == AT_DECLARE) {
			if (tree->var == NULL) {
				tree->var = node;
				return 0;
			} else {
				return add(tree->var, node,0);
			}
		}

		//manage the forward definition
		if (nodeType == AT_FUNC ){
			SYMTREE forward = searchForwardFunction(tree,node);
			if(forward != NULL){// if found a function matching the criteria
				if(!forward->isForward)
					return Error(1809,"You cannot define two time the same function.");
				else{
					forward->ASTaddr = node->ASTaddr;
					forward->isForward = 0;
					return 0;
				}

			}
		}

		// if add the first sub function for the function
		if (nodeType == AT_FUNC && deeper) {
			if (tree->func == NULL) {
				node->parent = tree;
				tree->func = node;
				node->previous = NULL;
				return 0;
			} else {
				return add(tree->func, node, 0);
			}
		}else{
			if (tree->next == NULL) {
				node->parent = tree->parent;
				tree->next = node;
				node->previous = tree;
			} else {
				return add(tree->next, node,0);
			}
		}
	} else { // if within a list of args/var/function

		if (tree->next == NULL) {
			tree->next = node;
			node->previous = tree;

		} else {
			return add(tree->next, node,0);
		}
		return 0;

	}
	return 0;

}


/** validate the argument base on a sym func node and asttree func call node*/
int sameArgsType(SYMTREE originalSym, SYMTREE sym, ASTTREE node){
	TraceMsg("sameArgsType","entry");
	if(sym == NULL && node == NULL) return FALSE;
	SYMTREE args = sym->args;
	SYMTREE funcCalled = NULL;
	SYMTREE var = NULL;
	int valueType = AT_UNKNOWN;
	int argType = AT_UNKNOWN;
	ASTTREE currentNode = node;
	while( args != NULL){
		// both list are empty
		if(args == NULL && node == NULL) return TRUE;
		// not both list are empty but one is empty
		if(args == NULL ^ node == NULL ) return FALSE;
		int nodeType = node->left->type;
		if(node->right != NULL && node->right->type == AT_FUNC_CALL)
			nodeType =AT_FUNC_CALL;

		TraceMsg2("sameArgsType","node type",humanReadableNodeType(nodeType));
		switch(nodeType){
		case AT_FUNC_CALL:
			funcCalled = searchFunctionCalled(originalSym,node->left);
			if(funcCalled == NULL) return FALSE;
			valueType = funcCalled->typeVal;
			break;
		case AT_ID:
			var = searchForVarOrArgWithoutType(sym,node->left->sval);
			TraceMsg2("valueType",node->left->sval,(var==NULL)?"the var is null":"the is not null");
			if(var == NULL) return FALSE;
			valueType = var->typeVal;
			break;
		default:
			valueType = node->left->vtype;
			break;
		}

		argType = (args->typeVal != AT_NEG)?args->typeVal:AT_NB;
		TraceMsg2("sameArgsType",humanReadableNodeType(argType),humanReadableNodeType(valueType));
		if(valueType != argType )return FALSE;
		args = args->next;
		node = node->right;
	}
	return TRUE;
}

/**
 *
 */
SYMTREE searchFunctionDefinition(SYMTREE sym, ASTTREE node){
	// NULL check
	if (node == NULL || sym == NULL)
		return NULL;

	int symType = sym->nodeType;
	int nodeType = node->type;

	// When the AST node is AT_FUNC the strcmp between AST and sym are : node->left->right->sval
	// When the AT_FUNC_CALL node is given as parameter the lookup is different

	if (symType != AT_FUNC && nodeType != AT_FUNC)
		return NULL;

	// Check the main func
	if (node->left != NULL && strcmp(sym->sval, node->left->right->sval) == 0)
		return sym;

	SYMTREE subFunction = sym->func;
	while (subFunction != NULL) {
		if (node->left != NULL
				&& strcmp(subFunction->sval, node->left->right->sval) == 0
				&& subFunction->typeVal == node->left->vtype
				&& sameArgs(subFunction->args, node->left->left))
			return subFunction;

		subFunction = subFunction->next;
	}
	return NULL;

}


SYMTREE searchFuncInPreviousAndParent(const SYMTREE originalSym, SYMTREE sym, ASTTREE node){
	if(sym == NULL || node == NULL) return NULL;
	if(sym->nodeType != AT_FUNC) return NULL;
	TraceMsg("searchFuncInPreviousAndParent","entry");
	if(strcmp(sym->sval,node->left->sval)==0){
		if(sameArgsType(originalSym,sym,node->right) == TRUE)
			return sym;
	}
	TraceMsg("searchFuncInPreviousAndParent","search in previous");
	printSymTree(sym);
	if(sym->previous != NULL){
		TraceMsg("searchFuncInPreviousAndParent","there is previous");
		return searchFuncInPreviousAndParent(originalSym,sym->previous,node);
	}
	TraceMsg("searchFuncInPreviousAndParent","search in parent");
	if(sym->parent != NULL){
		TraceMsg("searchFuncInPreviousAndParent","parent not null");
		return searchFuncInPreviousAndParent(originalSym,sym->parent,node);
	}
	return NULL;
}


/** search a specific function based on the astnode of the function
 * in the list of all the function of the current sym. the search is limited
 * to one level don't go deeper in the tree
 */
SYMTREE searchFunctionCalled(SYMTREE sym, ASTTREE node) {
	// NULL check
	if (node == NULL || sym == NULL)
		return NULL;

	int symType = sym->nodeType;
	int nodeType = node->type;

	if (nodeType == AT_FUNC_CALL) // The compilator has received the name of the func call and try to locate one in the sym table
	{
		TraceMsg2("searchFunctionCalled","start searching",node->left->sval);
		TraceMsg("currentNode",humanReadableNodeType(sym->nodeType));
		SYMTREE subFunction = sym->func;

		while (subFunction != NULL) {
			TraceMsg2("[Name]Compare ", subFunction->sval,node->left->sval);
			if (strcmp(subFunction->sval, node->left->sval) == 0
					&& sameArgsType(sym,subFunction, node->right) == TRUE)
				return subFunction;

			subFunction = subFunction->next;
		}

		return searchFuncInPreviousAndParent(sym,sym,node);
	}
	return NULL;

}



int sameArgs(SYMTREE tree, ASTTREE node){
	if(tree == NULL && node == NULL) return TRUE;
	if(tree == NULL ^ node == NULL) return FALSE;
	if(tree->typeVal != node->vtype) return FALSE;
	return sameArgs(tree->next,node->right);
}

/**
 *
 */
SYMTREE searchVarAtCurrentLevel(SYMTREE tree, char * name){
	// NULL check
		if (name == NULL || tree == NULL)
					return NULL;
		TraceMsg("searchVarAtCurrentLevel","null ok");
		int treeType = tree->nodeType;
		if (treeType != AT_FUNC)
			return NULL;
		TraceMsg("searchVarAtCurrentLevel","check ok");
		//look in the variable
		SYMTREE var = tree->var;
		while (var != NULL) {
			if (strcmp(var->sval, name)==0) {
				return var;
			}
			var = var->next;
		}
		TraceMsg("searchVarAtCurrentLevel","not in var");
		//look in the argument
		SYMTREE argument = tree->args;
		while (argument != NULL) {
			if(strcmp(argument->sval,name)==0){
				return argument;
			}
			argument = argument->next;
		}
		TraceMsg("searchVarAtCurrentLevel","not in args");
		return NULL;
}

/** Searching for variables only based on its name and its type
 * if the variable doesn't exist in the current context it will look
 * in the inherited variable.
 */
SYMTREE varSearch(SYMTREE tree, char * name, int type) {
		// NULL check
		if (name == NULL || tree == NULL)
			return NULL;

		int treeType = tree->nodeType;
		if (treeType != AT_FUNC)
			return NULL;

		SYMTREE var = tree->var;
		while (var != NULL) {
			if(strcmp(var->sval,name) == 0){
				if(var->typeVal == type)
					return var;
				else
					/* return null because no other variable
					 * can have the same name but not the same type */
					return NULL;
			}
			var = var->next;
		}

		/* if the variable is not found look in the inherited
		 * var from the parent */
		if(tree->parent != NULL)
			return varSearch(tree->parent,name,type);
		return NULL;
}


/** Searching for variables only based on its name and its type
 * if the variable doesn't exist in the current context it will look
 * in the inherited variable.
 */
SYMTREE varSearchWithoutType(SYMTREE tree, char * name) {
		// NULL check
		if (name == NULL || tree == NULL)
			return NULL;

		int treeType = tree->nodeType;
		if (treeType != AT_FUNC)
			return NULL;

		SYMTREE var = tree->var;
		while (var != NULL) {
			if(strcmp(var->sval,name) == 0){
					return var;
			}
			var = var->next;
		}

		/* if the variable is not found look in the inherited
		 * var from the parent */
		if(tree->parent != NULL)
			return varSearchWithoutType(tree->parent,name);
		return NULL;
}


/** Will look for a variable but taking arguments as variable.
 *  Based on the name and the type it search first with in the current
 *  function variable, then within the argument of the function
 *  and finally within the inherited variables.
 */
SYMTREE searchForVarOrArgWithoutType(SYMTREE tree, char * name){
	// NULL check
	if (name == NULL || tree == NULL)return NULL;

	int treeType = tree->nodeType;
	if (treeType != AT_FUNC) return NULL;

	//look in the variable
	SYMTREE var = tree->var;
	while (var != NULL) {
		if (strcmp(var->sval, name) == 0) {
				return var;
		}
		var = var->next;
	}

	//look in the argument
	SYMTREE argument = tree->args;
	while (argument != NULL) {
		if(strcmp(argument->sval,name)==0){
			return argument;
		}
		argument = argument->next;
	}

	/* if var not found till now look in the inherited variables*/
	if(tree->parent != NULL)
		return searchForVarOrArgWithoutType(tree->parent,name);

	return NULL;
}

/** search a specific argument based on the astnode of the function
 * in the list of all the arguments of the current sym.
 */
SYMTREE argsSearch(SYMTREE tree, ASTTREE node) {
	// NULL check
	if (node == NULL || tree == NULL)
		return NULL;

	int treeType = tree->nodeType;
	int nodeType = node->type;
	if (treeType != AT_FUNC && nodeType != AT_ARGS )
		return NULL;

	SYMTREE argument = tree->func;
	while (argument != NULL) {
		if(strcmp(argument->sval,node->left->sval) == 0 &&	argument->typeVal == node->vtype)
			return argument;
		argument = argument->next;
	}
	return NULL;
}

/** count the number of argument */
int numberOfArguments(SYMTREE tree){
	if(tree == NULL) return 0;
	if(tree->nodeType != AT_FUNC) return 0;

	int count = 0;
	SYMTREE argument = tree->args;
	while(argument != NULL){
		count++;
		argument = argument->next;
	}
	return count;
}

/** free the sym structure */
void freeSymTree(SYMTREE symTree) {
if (symTree == NULL)
	return;
if (symTree->args != NULL)
	freeSymTree(symTree->args);
if (symTree->var != NULL)
	freeSymTree(symTree->var);
if (symTree->func != NULL)
	freeSymTree(symTree->func);
free(symTree);
}



int LocationSearch(SYMTREE tree, ASTTREE node) {
	// NULL check
	if (node == NULL || tree == NULL)
		return -1;
		
	  char myBuf[20];  
	  
	SYMTREE subFunction = tree->func;
	while (subFunction != NULL)
	 {				    
		   sprintf(myBuf, "%p",  node );
		   if(strcmp(subFunction->ASTaddr,myBuf)==0) 
		   return subFunction->location;
			
			    SYMTREE nextFunction = subFunction->next;
		    	while(nextFunction != NULL)
		    	{				
					  sprintf(myBuf, "%p",  node );
			   		if(strcmp(nextFunction->ASTaddr,myBuf)==0) 
						return nextFunction->location;
						nextFunction=nextFunction->next;
				}

		subFunction = subFunction->next;
	}
	return -1;

}


int TypeSearch(SYMTREE tree, ASTTREE node) {
	// NULL check
	if (node == NULL || tree == NULL)
		return -1;
	 
	  char myBuf[20];  
	  
	SYMTREE subFunction = tree->func;
	while (subFunction != NULL)
	 {			    
		   sprintf(myBuf, "%p",  node );
			   if(strcmp(subFunction->ASTaddr,myBuf)==0) 
			   return subFunction->typeVal;
			
			    SYMTREE nextFunction = subFunction->next;
		    	while(nextFunction != NULL)
		    	{
				
					  sprintf(myBuf, "%p",  node );
			   		if(strcmp(nextFunction->ASTaddr,myBuf)==0) 
						return nextFunction->typeVal;
						nextFunction=nextFunction->next;
				}

		subFunction = subFunction->next;
	}
	return -1;

}


int getMaxMemoryUsage(SYMTREE s ) {
// NULL check
int max=0;
	 
		
	SYMTREE subFunction = s->func;
	while (subFunction != NULL) 
	{
		SYMTREE nextFunction = s->next;
			while(nextFunction != NULL){
			 		max++;
			 		nextFunction=nextFunction->next;
			}
			
 
		subFunction = subFunction->next;
	}
	return max;
}

